import { initWebGPU } from '../_shared'
import fragmentCode from '@shaders/traingle/demo2/traingle.frag.wgsl?raw'
import vertexCode from '@shaders/traingle/demo2/traingle.vert.wgsl?raw'

await run()

async function run() {
  const { adapter, device, format, context } =
    await initWebGPU(document.querySelector('canvas') as HTMLCanvasElement)
  const { pipeline, vertexObj, colorObj } = await initGPUPipeline({
    device,
    format,
    vertexCode,
    fragmentCode,
  })
  const draw1 = () => draw({
    device,
    context,
    pipeline,
    vertexObj,
    colorObj,
  })
  document.querySelector('#rangeX')
    ?.addEventListener('input', (e) => {
      const x = (+(e.target! as HTMLInputElement).value / 100) - 0.5
      vertexObj.vertex[0] = -0.5 + x
      vertexObj.vertex[3] = x
      vertexObj.vertex[6] = 0.5 + x
      device.queue.writeBuffer(vertexObj.vertexBuffer, 0, vertexObj.vertex)
      draw1()
    })
  document.querySelector('#rangeY')
    ?.addEventListener('input', (e) => {
      const y = (+(e.target! as HTMLInputElement).value / 100) - 0.5
      vertexObj.vertex[1] = -0.5 + y
      vertexObj.vertex[4] = 0.5 + y
      vertexObj.vertex[7] = -0.5 + y
      device.queue.writeBuffer(vertexObj.vertexBuffer, 0, vertexObj.vertex)
      draw1()
    })
  document.querySelector('input[type=color]')
    ?.addEventListener('input', (e) => {
      const color = (e.target! as HTMLInputElement).value
      const r = +('0x' + color.slice(1,3)) / 255
      const g = +('0x' + color.slice(3,5)) / 255
      const b = +('0x' + color.slice(5,7)) / 255
      colorObj.color[0] = r
      colorObj.color[1] = g
      colorObj.color[2] = b
      device.queue.writeBuffer(colorObj.colorBuffer, 0, colorObj.color)
      draw1()
    })
  await draw1()
}

interface InitGPUPipelineProps {
  device: GPUDevice;
  format: GPUTextureFormat;
  vertexCode: string;
  fragmentCode: string;
}

async function initGPUPipeline({ device, format, vertexCode, fragmentCode }: InitGPUPipelineProps) {
  const vertex = new Float32Array([
    -0.5, -0.5, 0,
    0.0, 0.5, 0,
    0.5, -0.5, 0,
  ])
  const vertexBuffer = device.createBuffer({
    size: vertex.byteLength,
    usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
  })
  device.queue.writeBuffer(vertexBuffer, 0, vertex)
  const color = new Float32Array([
    1, 1, 1, 1,
  ])
  const colorBuffer = device.createBuffer({
    size: color.byteLength,
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
  })
  device.queue.writeBuffer(colorBuffer, 0, color)
  const vertexShaderModule = device.createShaderModule({
    code: vertexCode,
  })
  const fragmentShaderModule = device.createShaderModule({
    code: fragmentCode,
  })
  const pipeline = await device.createRenderPipelineAsync({
    layout: 'auto',
    vertex: {
      module: vertexShaderModule,
      entryPoint: 'main',
      buffers: [
        {
          arrayStride: 3 * vertex.BYTES_PER_ELEMENT,
          attributes: [
            {
              shaderLocation: 0,
              offset: 0,
              format: 'float32x3',
            },
          ],
        },
      ],
    },
    fragment: {
      module: fragmentShaderModule,
      entryPoint: 'main',
      targets: [
        {
          format,
        },
      ],
    },
    primitive: {
      topology: 'triangle-list',
    },
  })
  const group = device.createBindGroup({
    layout: pipeline.getBindGroupLayout(0),
    entries: [{
      binding: 0,
      resource: {
        buffer: colorBuffer
      }
    }]
  })
  const vertexObj = {
    vertex, vertexBuffer,
  }
  const colorObj = {
    color, colorBuffer, group
  }
  return { pipeline, vertexObj, colorObj }
}

interface DrawProps {
  device: GPUDevice;
  context: GPUCanvasContext;
  pipeline: GPURenderPipeline;
  vertexObj: {
    vertex: Float32Array;
    vertexBuffer: GPUBuffer;
  },
  colorObj: {
    color: Float32Array;
    colorBuffer: GPUBuffer;
    group: GPUBindGroup;
  }
}

async function draw({ device, context, pipeline, vertexObj, colorObj }: DrawProps) {
  const encoder = device.createCommandEncoder()
  const renderPass = encoder.beginRenderPass({
    colorAttachments: [
      {
        view: context.getCurrentTexture().createView(),
        loadOp: 'clear',
        clearValue: { r: 0, g: 0, b: 0, a: 1 },
        storeOp: 'store',
      },
    ],
  })
  renderPass.setVertexBuffer(0, vertexObj.vertexBuffer)
  renderPass.setBindGroup(0, colorObj.group)
  renderPass.setPipeline(pipeline)
  renderPass.draw(3)
  renderPass.end()
  const buffer = encoder.finish()
  device.queue.submit([buffer])
}